package com.atguigu.guli.service.ucenter.service.impl;

import com.atguigu.guli.common.util.utils.FormUtils;
import com.atguigu.guli.common.util.utils.HttpClientUtils;
import com.atguigu.guli.common.util.utils.MD5;
import com.atguigu.guli.service.base.consts.ServiceConsts;
import com.atguigu.guli.service.base.exception.GuliException;
import com.atguigu.guli.service.base.result.ResultCodeEnum;
import com.atguigu.guli.service.base.utils.JwtHelper;
import com.atguigu.guli.service.base.utils.JwtInfo;
import com.atguigu.guli.service.ucenter.entity.Member;
import com.atguigu.guli.service.ucenter.entity.form.MemberRegistForm;
import com.atguigu.guli.service.ucenter.mapper.MemberMapper;
import com.atguigu.guli.service.ucenter.properties.WxProperties;
import com.atguigu.guli.service.ucenter.service.MemberService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpSession;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 会员表 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2021-08-10
 */
@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {

    @Autowired
    RedisTemplate redisTemplate;
    @Override
    public void regist(MemberRegistForm member) {
        //1、检查参数格式是否正确
        String code = member.getCode();
        String mobile = member.getMobile();
        String nickname = member.getNickname();
        String password = member.getPassword();
        if (StringUtils.isEmpty(code)||
                StringUtils.isEmpty(mobile)||
                StringUtils.isEmpty(nickname)||
                StringUtils.isEmpty(password)||
                !FormUtils.isMobile(mobile) //手机号码不为空的情况下格式错误
        ){
            throw new GuliException(ResultCodeEnum.PARAM_ERROR,new RuntimeException(ResultCodeEnum.PARAM_ERROR.getMessage()));
        }
        //2、判断验证码是否正确
        String redisCodeKey = ServiceConsts.MOBILE_CODE_PREFIX +mobile;
        Object redisCode = redisTemplate.opsForValue().get(redisCodeKey);

        if (redisCode==null || !code.equals(redisCode.toString())){
            throw new GuliException(ResultCodeEnum.CODE_ERROR,new RuntimeException(ResultCodeEnum.CODE_ERROR.getMessage()));
        }
        //3、处理注册的业务
        //手机号码唯一校验
        LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Member::getMobile,mobile);
        Integer count = baseMapper.selectCount(queryWrapper);
        if (count>0){
            throw new GuliException(ResultCodeEnum.REGISTER_MOBLE_ERROR,new RuntimeException(ResultCodeEnum.REGISTER_MOBLE_ERROR.getMessage()));
        }
        //密码加密
        Member m = new Member();
        m.setNickname(nickname);
        m.setPassword(MD5.encrypt(password));
        m.setMobile(mobile);
        m.setAvatar("https://img1.baidu.com/it/u=283284588,2796778480&fm=26&fmt=auto&gp=0.jpg");
        baseMapper.insert(m);
        //删除redis中的注册验证码
        redisTemplate.delete(redisCodeKey);
    }

    @Override
    public String login(String mobile, String password) {
        //MD5:同一个字符串每次获取的加密结果都一样
        //密码可以加盐保存 或者 SpringSecurity： BCryptPasswordEncoder: 同一个字符串每次加密的结果都不一样，需要使用它的方法进行校验密码是否正确
        LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();
        //根据账号查询对象
        queryWrapper.eq(Member::getMobile,mobile);
        Member member = baseMapper.selectOne(queryWrapper);
        if (member==null){
            throw  new GuliException(ResultCodeEnum.LOGIN_MOBILE_ERROR);
        }
        //根据查询到的对象验证密码是否正确
        if (!MD5.encrypt(password).equals(member.getPassword())){
            throw new GuliException(ResultCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        //将member对象的数据转为jwt的 token

        JwtInfo jwtInfo=new JwtInfo();
        jwtInfo.setId(member.getId());
        jwtInfo.setNickname(member.getNickname());
        jwtInfo.setAvatar(member.getAvatar());
        String token = JwtHelper.createToken(jwtInfo);
        return token;
    }

    @Autowired
    WxProperties wxProperties;
    @Override
    public String wxLogin(HttpSession session) {
        try {
            //让浏览器访问wx授权服务器获取wx登录二维码
            String url = "https://open.weixin.qq.com/connect/qrconnect?" +
                    "appid=%s" + //wxed9954c01bb89b47
                    //http://localhost:8160/api/ucenter/wx/callback8160
                    "&redirect_uri=%s" +//回调地址的域名必须和wx平台中配置的授权回调与一致
                    "&response_type=code" +
                    "&scope=snsapi_login" +
                    "&state=%s" + //随机字符串，防止恶意攻击[wx在回调时会将state的值传入]
                    "#wechat_redirect";
            String redirectUri = URLEncoder.encode(wxProperties.getRedirectUri(), "UTF-8");
            String state = UUID.randomUUID().toString().replace("-", "");
            session.setAttribute("state",state);

            url = String.format(url,wxProperties.getAppId(),redirectUri,state);
            //由springmvc解析：如果解析字符串以redirect：表示  是一个重定向视图响应
            //如果解析字符串没有redirect，表示是一个转发视图对象
            return "redirect:"+url;//相应重定向视图
        } catch (Exception e) {
//            e.printStackTrace();
            throw new GuliException(ResultCodeEnum.UNKNOWN_REASON,e);
        }
    }

    @Override
    public String wxCallback(String code, String state, HttpSession session) {
        //校验state是否正确
        Object sessionState = session.getAttribute("state");
        if (StringUtils.isEmpty(state)||sessionState==null|| !state.equals(sessionState.toString())){
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }
        //根据code值获取wx用户的数据: 相当于用户的授权
        //app的身份： appId+appSecurity:
        /*
            # 微信开放平台 appid
            appId: wxed9954c01bb89b47
            # 微信开放平台 appsecret
            appSecret: a7482517235173ddb4083788de60b90e
         */
        try {
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                    "appid="+wxProperties.getAppId() +
                    "&secret="+wxProperties.getAppSecret() +
                    "&code="+code +
                    "&grant_type=authorization_code";
            System.out.println(code);
            //java代码中访问  获取accessToken的接口地址  获取授权码字符串
            //创建客户端对象
            HttpClientUtils client = new HttpClientUtils(url);
            //发起请求
            client.get();
            //获取响应结果
            String content = client.getContent();
            System.out.println(content);
            Gson gson = new Gson();
            Map map = gson.fromJson(content, Map.class);
            if (map.get("errcode")!=null){
                throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
            }
            String accessToken = map.get("access_token").toString();
            String openid = map.get("openid").toString();
            //判断数据库中是否有openidwx用户的数据，如果没有，将他保存到我们的数据库中，如果有直接获取数据库中wx用户数据显示

            LambdaQueryWrapper<Member> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(Member::getOpenid,openid);
            Member member = this.getOne(queryWrapper);
            if (member==null){
                //查询wx平台用户数据
                System.out.println("openid = " + openid);
                System.out.println("accessToken = " + accessToken);
                //使用openid和accessToken获取用户在wx中的数据
                url = "https://api.weixin.qq.com/sns/userinfo?" +
                        "access_token="+accessToken +
                        "&openid="+openid;
                //创建客户端对象
                client = new HttpClientUtils(url);
                //发送请求
                client.get();
                //获取响应结果
                content = client.getContent();
                System.out.println("content = " + content);
                map=gson.fromJson(content,Map.class);
                if (map.get("errcode")!=null){
                    throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
                }
                String nickname = map.get("nickname").toString();
                Integer sex = new BigDecimal(map.get("sex").toString()).intValue();
                String headimgurl = map.get("headimgurl").toString();
                //将用户数据存到数据库
                member = new Member();
                member.setAvatar(headimgurl);
                member.setNickname(nickname);
                member.setOpenid(openid);
                member.setSex(sex);
                this.save(member);
                System.out.println(nickname+"用户第一次使用wx登陆在线教育");
            }else {
                System.out.println(member.getNickname()+"用户第n次使用wx登录在线教育");
                //判断member对象的更新时间 和当前的时间日期比较，如果超过三天 再次查询wx后台获取wx用户端的数据更新数据库
                //解决缓存问题 头像等
                //Date gmtModified = member.getGmtModified();
            }
            JwtInfo jwtInfo = new JwtInfo();
            jwtInfo.setAvatar(member.getAvatar());
            jwtInfo.setNickname(member.getNickname());
            jwtInfo.setId(member.getId());//id必须使用数据库中用户的id，表示在线教育唯一的一个用户
            String token = JwtHelper.createToken(jwtInfo);
            //如果授权登录，获取到wx用户的数据，让用户跳转到登录成功页面[loaclhost：3000]
            return "redirect:http://localhost:3000?token="+token;
        } catch (Exception e) {
//            e.printStackTrace();
            throw new GuliException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR,e);
        }
    }
}
